<template>
  <div
    class="slider__button-wrapper"
    @mouseenter="handleMouseEnter"
    @mouseleave="handleMouseLeave"
    @mousedown="onButtonDown"
    @touchstart="onButtonDown"
    :class="{ 'hover': hovering, 'dragging': dragging, 'undraggable': undraggable }"
    :style="wrapperStyle"
    ref="button"
    tabindex="0"
    @focus="handleMouseEnter"
    @blur="handleMouseLeave"
    @keydown.left="onLeftKeyDown"
    @keydown.right="onRightKeyDown"
    @keydown.down.prevent="onLeftKeyDown"
    @keydown.up.prevent="onRightKeyDown"
  >
    <el-tooltip
      placement="top"
      ref="tooltip"
      :popper-class="tooltipClass"
      :disabled="!showTooltip">
      <span slot="content">{{ formatValue }}</span>
      <div class="slider__button" :class="{ 'hover': hovering, 'dragging': dragging, 'el-icon-loading': isPause }"></div>
    </el-tooltip>
  </div>
</template>

<script>

export default {
  name: 'TimelineSliderButton',
  props: {
    value: {
      type: Number,
      default: 0,
    },
    vertical: {
      type: Boolean,
      default: false,
    },
    // 不可拖动的
    undraggable: {
      type: Boolean,
      default: false,
    },
    isPause: {
      type: Boolean,
      default: false,
    },
    tooltipClass: String,
  },
  data() {
    return {
      hovering: false,
      dragging: false,
      isClick: false,
      startX: 0,
      currentX: 0,
      startY: 0,
      currentY: 0,
      startPosition: 0,
      newPosition: null,
      oldValue: this.value,
    };
  },
  computed: {
    disabled() {
      return this.$parent.sliderDisabled;
    },
    max() {
      return this.$parent.max;
    },
    min() {
      return this.$parent.min;
    },
    step() {
      return this.$parent.step;
    },
    showTooltip() {
      return this.$parent.showTooltip;
    },
    precision() {
      return this.$parent.precision;
    },
    currentPosition() {
      return `${(this.value - this.min) / (this.max - this.min) * 100}%`;
    },
    enableFormat() {
      return this.$parent.formatTooltip instanceof Function;
    },
    formatValue() {
      return this.enableFormat && this.$parent.formatTooltip(this.value) || this.value;
    },
    wrapperStyle() {
      return this.vertical ? { bottom: this.currentPosition } : { left: this.currentPosition };
    },
  },
  watch: {
    dragging(val) {
      this.$parent.dragging = val;
    },
  },
  methods: {
    displayTooltip() {
      this.$refs.tooltip && (this.$refs.tooltip.showPopper = true);
    },
    hideTooltip() {
      this.$refs.tooltip && (this.$refs.tooltip.showPopper = false);
    },
    handleMouseEnter() {
      this.hovering = true;
      this.displayTooltip();
    },
    handleMouseLeave() {
      this.hovering = false;
      this.hideTooltip();
    },
    onButtonDown(event) {
      if (this.undraggable) {
        console.log('[禁止拖动]');
        return;
      }
      if (this.disabled) return;
      event.preventDefault();
      this.onDragStart(event);
      window.addEventListener('mousemove', this.onDragging);
      window.addEventListener('touchmove', this.onDragging);
      window.addEventListener('mouseup', this.onDragEnd);
      window.addEventListener('touchend', this.onDragEnd);
      window.addEventListener('contextmenu', this.onDragEnd);
    },
    onLeftKeyDown() {
      if (this.undraggable) {
        console.log('[禁止拖动]');
        return;
      }
      if (this.disabled) return;
      this.newPosition = parseFloat(this.currentPosition) - this.step / (this.max - this.min) * 100;
      this.setPosition(this.newPosition);
      this.$parent.emitChange();
    },
    onRightKeyDown() {
      if (this.undraggable) {
        console.log('[禁止拖动]');
        return;
      }
      if (this.disabled) return;
      this.newPosition = parseFloat(this.currentPosition) + this.step / (this.max - this.min) * 100;
      this.setPosition(this.newPosition);
      this.$parent.emitChange();
    },
    onDragStart(event) {
      this.dragging = true;
      this.isClick = true;
      if (event.type === 'touchstart') {
        event.clientY = event.touches[0].clientY;
        event.clientX = event.touches[0].clientX;
      }
      if (this.vertical) {
        this.startY = event.clientY;
      } else {
        this.startX = event.clientX;
      }
      this.startPosition = parseFloat(this.currentPosition);
      this.newPosition = this.startPosition;
    },
    onDragging(event) {
      if (this.dragging) {
        this.isClick = false;
        this.displayTooltip();
        this.$parent.resetSize();
        let diff = 0;
        if (event.type === 'touchmove') {
          event.clientY = event.touches[0].clientY;
          event.clientX = event.touches[0].clientX;
        }
        if (this.vertical) {
          this.currentY = event.clientY;
          diff = (this.startY - this.currentY) / this.$parent.sliderSize * 100;
        } else {
          this.currentX = event.clientX;
          diff = (this.currentX - this.startX) / this.$parent.sliderSize * 100;
        }
        this.newPosition = this.startPosition + diff;
        this.setPosition(this.newPosition);
      }
    },
    onDragEnd() {
      if (this.dragging) {
        /*
         * 防止在 mouseup 后立即触发 click，导致滑块有几率产生一小段位移
         * 不使用 preventDefault 是因为 mouseup 和 click 没有注册在同一个 DOM 上
         */
        setTimeout(() => {
          this.dragging = false;
          this.hideTooltip();
          if (!this.isClick) {
            this.setPosition(this.newPosition);
            this.$parent.emitChange();
          }
        }, 0);
        window.removeEventListener('mousemove', this.onDragging);
        window.removeEventListener('touchmove', this.onDragging);
        window.removeEventListener('mouseup', this.onDragEnd);
        window.removeEventListener('touchend', this.onDragEnd);
        window.removeEventListener('contextmenu', this.onDragEnd);
      }
    },
    setPosition(newPosition) {
      if (newPosition === null || isNaN(newPosition)) return;
      if (newPosition < 0) {
        newPosition = 0;
      } else if (newPosition > 100) {
        newPosition = 100;
      }
      const lengthPerStep = 100 / ((this.max - this.min) / this.step);
      const steps = Math.round(newPosition / lengthPerStep);
      let value = steps * lengthPerStep * (this.max - this.min) * 0.01 + this.min;
      value = parseFloat(value.toFixed(this.precision));
      this.$emit('input', value);
      this.$nextTick(() => {
        this.displayTooltip();
        this.$refs.tooltip && this.$refs.tooltip.updatePopper();
      });
      if (!this.dragging && this.value !== this.oldValue) {
        this.oldValue = this.value;
      }
    },
  },
};
</script>

<style lang="scss" scoped>
.slider__button-wrapper {
  height: 36px;
  width: 36px;
  position: absolute;
  z-index: 1001;
  top: -15px;
  transform: translateX(-50%);
  background-color: transparent;
  text-align: center;
  user-select: none;

  &::after {
    content: '';
    height: 100%;
    vertical-align: middle;
    display: inline-block;
  }

  ::v-deep .el-tooltip {
    vertical-align: middle;
    display: inline-block;
  }

  &:not(.undraggable):hover {
    cursor: grab;

    .slider__button {
      transform: scale(1.2);
    }
  }

  &:not(.undraggable).dragging {
    cursor: grabbing;
  }

  &.undraggable {
    cursor: default;
  }

  .slider__button {
    width: 16px;
    height: 16px;
    border: 2px solid #409EFF;
    background-color: #FFF;
    border-radius: 50%;
    transition: .2s;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;

    font-size: 12px;
    color: #409EFF;
  }
}
</style>
